using System.Text;
using Microsoft.EntityFrameworkCore;
using RAGApp.Api.Data;
using RAGApp.Api.Models;
using RAGApp.Api.Models.DTOs;
using RAGApp.Api.Services.Interfaces;
using UglyToad.PdfPig;
using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor;
using DocumentFormat.OpenXml.Packaging;
using Markdig;
using Pgvector;

namespace RAGApp.Api.Services;

public class DocumentService : IDocumentService
{
    private readonly ApplicationDbContext _context;
    private readonly IEmbeddingService _embeddingService;
    private readonly ILogger<DocumentService> _logger;
    private const int MaxChunkSize = 500; // 每个文本块的最大字符数

    public DocumentService(
        ApplicationDbContext context,
        IEmbeddingService embeddingService,
        ILogger<DocumentService> logger)
    {
        _context = context;
        _embeddingService = embeddingService;
        _logger = logger;
    }

    public async Task<DocumentResponseDto> UploadDocumentAsync(DocumentUploadDto dto)
    {
        try
        {
            _logger.LogInformation($"开始处理文档: {dto.Title}");

            // 如果是文件上传
            string content;
            if (dto.File != null)
            {
                content = await ExtractTextFromFile(dto.File);
                _logger.LogInformation($"从文件中提取的文本长度: {content.Length}");
            }
            else
            {
                content = dto.Content ?? string.Empty;
            }

            // 创建文档记录
            var document = new Document
            {
                Title = dto.Title,
                Content = content,
                Source = dto.Source,
                CreatedAt = DateTime.UtcNow,
                UpdatedAt = DateTime.UtcNow
            };

            // 文本分块
            var chunks = await ChunkDocumentAsync(document);
            _logger.LogInformation($"文本已分成 {chunks.Count} 个块");

            // 为每个块生成向量
            var vectors = new List<float[]>();
            foreach (var chunk in chunks)
            {
                var vector = await _embeddingService.GenerateEmbeddingAsync(chunk.Content);
                vectors.Add(vector);
            }
            _logger.LogInformation($"成功生成 {vectors.Count} 个向量");

            // 更新文档块的向量
            for (int i = 0; i < chunks.Count; i++)
            {
                chunks[i].Embedding = new Pgvector.Vector(vectors[i]);
            }

            // 保存到数据库
            _context.Documents.Add(document);
            _context.DocumentChunks.AddRange(chunks);
            await _context.SaveChangesAsync();

            return new DocumentResponseDto
            {
                Id = document.Id,
                Title = document.Title,
                Content = document.Content,
                Source = document.Source,
                CreatedAt = document.CreatedAt,
                UpdatedAt = document.UpdatedAt,
                ChunksCount = chunks.Count
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "上传文档时发生错误");
            throw;
        }
    }

    public async Task<List<DocumentResponseDto>> GetAllDocumentsAsync()
    {
        var documents = await _context.Documents
            .Include(d => d.Chunks)
            .Select(d => new DocumentResponseDto
            {
                Id = d.Id,
                Title = d.Title,
                Content = d.Content,
                Source = d.Source,
                CreatedAt = d.CreatedAt,
                UpdatedAt = d.UpdatedAt,
                ChunksCount = d.Chunks.Count
            })
            .ToListAsync();

        return documents;
    }

    public async Task<DocumentResponseDto?> GetDocumentByIdAsync(Guid id)
    {
        var document = await _context.Documents
            .Include(d => d.Chunks)
            .FirstOrDefaultAsync(d => d.Id == id);

        if (document == null)
            return null;

        return new DocumentResponseDto
        {
            Id = document.Id,
            Title = document.Title,
            Content = document.Content,
            Source = document.Source,
            CreatedAt = document.CreatedAt,
            UpdatedAt = document.UpdatedAt,
            ChunksCount = document.Chunks.Count
        };
    }

    public async Task<bool> DeleteDocumentAsync(Guid id)
    {
        var document = await _context.Documents.FindAsync(id);
        if (document == null)
            return false;

        _context.Documents.Remove(document);
        await _context.SaveChangesAsync();
        return true;
    }

    public Task<List<DocumentChunk>> ChunkDocumentAsync(Document document, int chunkSize = 1000, int overlap = 200)
    {
        var chunks = new List<DocumentChunk>();
        var text = document.Content;

        // 按句子分割文本
        var sentences = text.Split(new[] { ". ", "。", "！", "？", "\n" }, StringSplitOptions.RemoveEmptyEntries)
            .Select(s => s.Trim())
            .Where(s => !string.IsNullOrWhiteSpace(s))
            .ToList();

        var currentChunk = new StringBuilder();
        foreach (var sentence in sentences)
        {
            // 如果当前块加上新句子不超过最大大小，就添加到当前块
            if (currentChunk.Length + sentence.Length + 1 <= chunkSize)
            {
                if (currentChunk.Length > 0)
                    currentChunk.Append(" ");
                currentChunk.Append(sentence);
            }
            else
            {
                // 如果当前块不为空，保存它
                if (currentChunk.Length > 0)
                {
                    chunks.Add(new DocumentChunk
                    {
                        Document = document,
                        DocumentId = document.Id,
                        Content = currentChunk.ToString(),
                        CreatedAt = DateTime.UtcNow
                    });
                }

                // 开始新的块
                currentChunk.Clear();
                currentChunk.Append(sentence);
            }
        }

        // 添加最后一个块
        if (currentChunk.Length > 0)
        {
            chunks.Add(new DocumentChunk
            {
                Document = document,
                DocumentId = document.Id,
                Content = currentChunk.ToString(),
                CreatedAt = DateTime.UtcNow
            });
        }

        return Task.FromResult(chunks);
    }

    private async Task<string> ExtractTextFromFile(IFormFile file)
    {
        var extension = Path.GetExtension(file.FileName).ToLower();
        
        using var stream = file.OpenReadStream();
        return extension switch
        {
            ".pdf" => await ExtractTextFromPdf(stream),
            ".docx" => await ExtractTextFromDocx(stream),
            ".md" => await ExtractTextFromMarkdown(stream),
            ".txt" => await ExtractTextFromTxt(stream),
            _ => throw new NotSupportedException($"不支持的文件类型: {extension}")
        };
    }

    private async Task<string> ExtractTextFromPdf(Stream stream)
    {
        var text = new StringBuilder();
        using (var pdfDocument = PdfDocument.Open(stream))
        {
            foreach (var page in pdfDocument.GetPages())
            {
                var pageText = await Task.Run(() => ContentOrderTextExtractor.GetText(page));
                text.AppendLine(pageText);
            }
        }
        return text.ToString();
    }

    private async Task<string> ExtractTextFromDocx(Stream stream)
    {
        var text = new StringBuilder();
        using (var doc = WordprocessingDocument.Open(stream, false))
        {
            var body = doc.MainDocumentPart?.Document.Body;
            if (body != null)
            {
                await Task.Run(() => text.Append(body.InnerText));
            }
        }
        return text.ToString();
    }

    private async Task<string> ExtractTextFromMarkdown(Stream stream)
    {
        using var reader = new StreamReader(stream);
        var markdown = await reader.ReadToEndAsync();
        var pipeline = new MarkdownPipelineBuilder().Build();
        var html = await Task.Run(() => Markdown.ToHtml(markdown, pipeline));
        return html;
    }

    private async Task<string> ExtractTextFromTxt(Stream stream)
    {
        using var reader = new StreamReader(stream);
        return await reader.ReadToEndAsync();
    }
} 